﻿using UnityEngine;
using System;
using System.Collections;
using Engine.Entities.Character;
using System.Collections.Generic;
using Assets.entities;

namespace Game.Scripts.Managers { 
    
    [Serializable]
    public class BuildingPrototype {
        public string name;
        public SkillsEnum preferredSkill;
        public uint minimumPopulation;
        public uint workersPerLevel;

        public Resource resourceProduced;
        
        [Serializable]
        public class LevelCostsWrapper
        {
            public List<Resource> resources = new List<Resource>();
        }
        public List<LevelCostsWrapper> buildingRefoundPerLevel = new List<LevelCostsWrapper>();
        public List<LevelCostsWrapper> buildingCostsPerLevel = new List<LevelCostsWrapper>();

        public GameObject prefab;
        public UIAtlas atlasName;
        public string iconName;

        [NonSerialized]
        internal int prototypeIndex;

        [NonSerialized]
        private List<Building> instances;

        [NonSerialized]
        internal BuildingManager manager;

        public BuildingPrototype() {
            this.instances = new List<Building>();
        }

        public int index {
            get {
                return this.prototypeIndex;
            }
        }

        public int instanceCount {
            get {
                return this.instances.Count;
            }
        }

        public Building getInstanceByIndex(int index) {
            return this.instances[index];
        }

        public Building instantiateBuildingAt(float _x, float _y, float _z, bool force = false)
        {
            //ricorsruisco il voxelHitPosition
            Vector3 voxelHitPosition = new Vector3(_x, _y, _z);
            GameObject result = null;

            //discretizzo x,y,z
            int x = (int)voxelHitPosition.x;
            int y = (int)voxelHitPosition.y;
            int z = (int)voxelHitPosition.z;

            if( !force )
            {
                if (this.manager.voxelWorld.hasVoxelAt(x, y, z))
                {
                    //Voxel voxelAt = this.manager.voxelWorld.getVoxelAt(x, y, z);
                    //MarchingCube cube = voxelAt.content.cube;
                    //bool isTopFull = voxelAt.content.cube.topMarchingSquare.isFull;
                    //bool isBottomFull = voxelAt.content.cube.bottomMarchingSquare.isFull;
                    //bool hasContent = voxelAt.content.informativeContent != null;

                    if (this.isBuildingAllowedAt(x, y, z))
                    {
                        result = instantiateBuilding();

                        if ( result == null )
                        {
                            Debug.LogError("Non è stato possibile costruire un/a " + this.name);
                            return null;
                        }

                        this.manager.voxelWorld.SetHeightOverMarchingCube(new Vector3(x, y, z), result);

                        //if (isTopFull)
                        //{
                        //    result.transform.position = voxelHitPosition + Vector3.up;
                        //}
                        //else if (cube.isIsosurface && isBottomFull)
                        //{
                        //    result.transform.position = voxelHitPosition + Vector3.up / 2;
                        //}

                        Voxel voxelAt = this.manager.voxelWorld.getVoxelAt(x,y,z);
                        voxelAt.content.informativeContent = result;
                    }

                }
            }
            else
            {
                //sto forzando l'inserimento, quindi non passo per il voxel world
                //Alcuni teorici direbbero che questo è posizionamento non informato
                //N.B. Non ci sarà nessun voxel che saprà dove si trova l'edificio
                result = instantiateBuilding(false);

                if (result == null)
                {
                    Debug.LogError("Non è stato possibile costruire un/a " + this.name);
                    return null;
                }

                result.transform.position = voxelHitPosition;
            }
            
            if (result != null)
            {
                //assegno il gameovject al parent
                result.transform.parent = this.manager.gameObject.transform;

                RefreshAStartGraph(result);

                return result.GetComponent<Building>();
            }
            else
                return null;
        }

        /// <summary>
        /// Rimuove un building dalla scena e dal controller
        /// Rende disoccupati tutti i lavoratori dell'edificio
        /// </summary>
        public void RemoveBuilding(Building building)
        {
            //TODO

            //rimuove dalla scena l'oggetto
            GameObject.Destroy(building.gameObject);

            //lo rimuove dalle istanze fabbricate
            instances.Remove(building);

            //rende disoccupati tutti i lavoratori dell'edificio
            foreach (SteveBrain brain in building.Workers)
            {
                brain.workplace = null;
            }

            //restituisce le risorse al giocatore
            Resource[] resources = GetListRefound((int)building.level);
            ResourceManager.instance.deposit(resources);

            //aggiorna il grafo dell'astar
            RefreshAStartGraph(building.gameObject);
        }

        public void RefreshAStartGraph(GameObject gameObject)
        {
            //aggiorno il grafo
            MeshFilter filter = gameObject.GetComponent<MeshFilter>();
            Bounds bounds = new Bounds();
            bounds.center = gameObject.transform.position + filter.mesh.bounds.center;
            bounds.size = filter.mesh.bounds.size;
            this.manager.pathfindObject.UpdateGraphs(bounds, 0);
        }

        public Resource[] GetListRefound(int level)
        {
            if (buildingRefoundPerLevel.Count < level)
            {
                Debug.LogError("Mancano i costi di refound per il livello " + level);
                return null;
            }
            return buildingRefoundPerLevel[level-1].resources.ToArray();
        }

        public Resource[] GetListCost(int level)
        {
            if (buildingCostsPerLevel.Count < level)
            {
                Debug.LogError( this.name +  ": Mancano i costi per il livello " + level);
                return null;
            }
            return buildingCostsPerLevel[level - 1].resources.ToArray();
        }

        /// <summary>
        /// Istanzia semplicemente un building
        /// Aggiunge il componente Building
        /// Aggiunge il componente per selezionare l'edificio
        /// Paga il costo delle risorse
        /// </summary>
        /// <returns></returns>
        private GameObject instantiateBuilding(bool pay = true)
        {
            GameObject instance = GameObject.Instantiate(prefab);

            Building building = instance.AddComponent<Building>();
            building._prototype = this;
            building._level = 1;

            instance.AddComponent<BoxCollider>();
            instance.AddComponent<BuildingMouseAction>();

            //paga
            if( pay )
            {
                Resource[] resources = GetListCost(1);
                ResourceManager.instance.withdraw(resources);
            }
            
            this.instances.Add(building);

            return instance;
        }

        /// <summary>
        /// Creo un edificio preview per collocare l'edificio all'interno dell'isola
        /// </summary>
        /// <returns></returns>
        internal GameObject InstantiateBuildingPreview(Vector3 pos)
        {
            GameObject instance = GameObject.Instantiate(prefab);

            Building building = instance.AddComponent<Building>();
            building._prototype = this;
            building._level = 1;

            this.manager.voxelWorld.SetHeightOverMarchingCube(pos, instance);
            
            return instance;
        }

        /// <summary>
        /// controlla il terreno e il costo della costruzione su una data cordinata
        /// </summary>
        internal bool isBuildingAllowedAt(int x, int y, int z)
        {
            //controllo il terreno
            Voxel voxelAt = this.manager.voxelWorld.getVoxelAt(x, y, z);

            if (voxelAt == null)
            {
                Debug.LogError("Non ho trovato nessun voxel at : " + x + "," + y + "," + z);
                return false;
            }

            MarchingCube cube = voxelAt.content.cube;
            bool hasTopVoxel = this.manager.voxelWorld.hasVoxelAt(x, y + 1, z);
            bool isTopFull = cube.topMarchingSquare.isFull;
            bool isTopEmpty = cube.topMarchingSquare.isEmpty;
            bool isBottomFull = cube.bottomMarchingSquare.isFull;
            bool hasContent = voxelAt.content.informativeContent != null;
            bool retval = false;

            if (!hasContent & voxelAt.prototype.solid)
            {
                if ((isTopFull && !hasTopVoxel) || (cube.isIsosurface && isBottomFull && isTopEmpty))
                {
                    retval = true;
                }
            }
            if (retval == false) return false;

            //controllo il costo per la costruzione
            Resource[] resources = GetListCost(1);                      //costo per un primo livello
            retval = ResourceManager.instance.hasAmounts(resources);
            
            return retval;
        }

        //public void SetHeightOverMarchingCube(Vector3 pos, GameObject instance)
        //{
        //    Voxel voxelAt = this.manager.voxelWorld.getVoxelAt((int)pos.x, (int)pos.y, (int)pos.z);
        //    MarchingCube cube = voxelAt.content.cube;
        //    bool isBottomFull = voxelAt.content.cube.bottomMarchingSquare.isFull;
        //    bool isTopFull = voxelAt.content.cube.topMarchingSquare.isFull;

        //    if (isTopFull)
        //    {
        //        instance.transform.position = pos + Vector3.up;
        //    }
        //    else if (cube.isIsosurface && isBottomFull)
        //    {
        //        instance.transform.position = pos + Vector3.up / 2;
        //    }
        //}
    }
}